/* ========================================
 *
 * Copyright YOUR COMPANY, THE YEAR
 * All Rights Reserved
 * UNPUBLISHED, LICENSED SOFTWARE.
 *
 * CONFIDENTIAL AND PROPRIETARY INFORMATION
 * WHICH IS THE PROPERTY OF your company.
 *
 * ========================================
*/

#include "beachday.h"

#define WS2812B_GPIO_SET     \
            ( LED_DATA_DR |= (uint32_t)(0x40) ) // LED_DATA__MASK
#define WS2812B_GPIO_CLR     \
            ( LED_DATA_DR &= (uint32_t)(0xBF) ) // ~LED_DATA__MASK

            
// module definitions
#define WS2812B_CFG_COLOR_OFF     { WS2812B_CFG_OFF, WS2812B_OFF, WS2812B_OFF, WS2812B_OFF }
#define WS2812B_CFG_COLOR_RED     { WS2812B_CFG_RED, WS2812B_LOW, WS2812B_OFF, WS2812B_OFF }
#define WS2812B_CFG_COLOR_GREEN   { WS2812B_CFG_GREEN, WS2812B_OFF, WS2812B_LOW, WS2812B_OFF }
#define WS2812B_CFG_COLOR_BLUE    { WS2812B_CFG_BLUE, WS2812B_OFF, WS2812B_OFF, WS2812B_LOW }
#define WS2812B_CFG_COLOR_YELLOW  { WS2812B_CFG_YELLOW, WS2812B_LOW, WS2812B_LOW, WS2812B_OFF }
#define WS2812B_CFG_COLOR_MAGENTA { WS2812B_CFG_MAGENTA, WS2812B_LOW, WS2812B_OFF, WS2812B_LOW }
#define WS2812B_CFG_COLOR_CYAN    { WS2812B_CFG_CYAN, WS2812B_OFF, WS2812B_LOW, WS2812B_LOW }
#define WS2812B_CFG_COLOR_WHITE   { WS2812B_CFG_WHITE, WS2812B_LOW, WS2812B_LOW, WS2812B_LOW }            

WS2812B_CONFIG wsSt[WS2812B_LED_COUNT] = { WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF,
                                           WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF,
                                           WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF,
                                           WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF,
                                           WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_OFF,
                                           WS2812B_CFG_COLOR_OFF
    };

WS2812B_CONFIG wsCFG[WS2812B_CFG_COUNT] = { WS2812B_CFG_COLOR_OFF, WS2812B_CFG_COLOR_RED, WS2812B_CFG_COLOR_GREEN,
                                            WS2812B_CFG_COLOR_BLUE, WS2812B_CFG_COLOR_YELLOW, WS2812B_CFG_COLOR_MAGENTA,
                                            WS2812B_CFG_COLOR_CYAN, WS2812B_CFG_COLOR_WHITE };

WS2812B_CONFIG* WS2812B_GetCFG( uint32 id )
{
    if(id >= WS2812B_CFG_COUNT)
    {
        return &wsCFG[0];
    }
    else
    {
        return &wsCFG[id];
    }
}

void SetWS2812B( uint32_t id, uint32_t cfg )
{
    if((id >= WS2812B_LED_COUNT) || (cfg >= WS2812B_CFG_COUNT))
    {
        wsSt[0] = *WS2812B_GetCFG(0);
    }
    else
    {
        wsSt[id] = *WS2812B_GetCFG(cfg);
    }
}

// module definitions
uint32_t WS2812BEnabled = TRUE;

// SM enforces max refresh rate of ~167Hz (30us per LED, 510us per refresh)
#define WS2812B_WAIT_TO_INIT           0   //  0 tick wait on entry to ST_INIT
#define WS2812B_WAIT_TO_PREFIX         1   //  1 ticks @1.024KHz wait on entry to ST_PREFIX
#define WS2812B_WAIT_TO_WRITE          0   //  0 ticks @1.024KHz wait on entry to ST_WRITE
#define WS2812B_WAIT_TO_IDLE           4   //  4 ticks @1.024KHz wait on entry to ST_IDLE

// module prototypes
void WS2812BWrite( WS2812B_CONFIG cfg );
inline void WS2812B_WriteWord( uint8_t sym );
inline void WS2812B_WriteSymZero( void );
inline void WS2812B_WriteSymOne( void );
inline void WS2812B_WriteWord_OFF( void );
inline void WS2812B_WriteWord_LOW( void );
inline void WS2812B_WriteWord_MED( void );
inline void WS2812B_WriteWord_HIGH( void );

// module loop
uint32_t ServWS2812B( void )
{
    
#define WS2812B_GPIO_INIT             do { LED_DATA_Write(0); } while (0)
#define WS2812B_GPIO_IDLE             do { LED_DATA_Write(0); } while (0)
#define WS2812B_GPIO_DISABLE          do { LED_DATA_Write(0); } while (0)
    
    static uint32_t st = WS2812B_ST_DISABLED, Stamp = 0;
    uint32_t i;
    
    if((!WS2812BEnabled) && (st != WS2812B_ST_DISABLED))
    {
        st = WS2812B_ST_SHDN;
    }
    
    /* STATE MACHINE FOR WS2812B
        during any cycle, if the WS2812BEnable is FALSE, force SM to SHDN if it's not already disabled
    
        DISABLED    (start), when enabled pass to init                                  -> INIT
        INIT        set initial GPIO                                                    -> PREFIX
        PREFIX      delay until reset is ensured                                        -> WRITE
        WRITE       write complete panel refresh (all LEDs)                             -> SUFFIX
        SUFFIX      set idle GPIO                                                       -> IDLE
        IDLE        delay until next refresh                                            -> PREFIX
        SHDN        disable supply for disabled                                         -> DISABLED
    */
    
    switch(st)
    {
        case WS2812B_ST_DISABLED:
            if(WS2812BEnabled)
            {
                Stamp = GetTick();
                st = WS2812B_ST_INIT;
            }
            break;
        case WS2812B_ST_INIT:
            if(GetTick() - Stamp >= WS2812B_WAIT_TO_INIT)
            {
                WS2812B_GPIO_INIT;
                
                Stamp = GetTick();
                st = WS2812B_ST_PREFIX;
            }
            break;
        case WS2812B_ST_PREFIX:
            if(GetTick() - Stamp >= WS2812B_WAIT_TO_PREFIX)
            {
                Stamp = GetTick();
                st = WS2812B_ST_WRITE;
            }
            break;
        case WS2812B_ST_WRITE:
            if(GetTick() - Stamp >= WS2812B_WAIT_TO_WRITE)
            {
                uint8  interruptState = CyEnterCriticalSection();
                
                for(i = 0; i < WS2812B_LED_COUNT; i++)
                {
                    WS2812BWrite(wsSt[i]);
                }
                
                CyExitCriticalSection(interruptState);
                
                LED_DATA_Write(GPIO_LOW); // set for reset
                CyDelayCycles(75 * SYS_FREQ_MHz);   // delay for 75us (greater than 50us)
                
                Stamp = GetTick();
                st = WS2812B_ST_SUFFIX;
            }
            break;
        case WS2812B_ST_SUFFIX:
            WS2812B_GPIO_IDLE;
            
            Stamp = GetTick();
            st = WS2812B_ST_IDLE;
            break;
        case WS2812B_ST_IDLE:
            if(GetTick() - Stamp >= WS2812B_WAIT_TO_IDLE)
            {
                Stamp = GetTick();
                st = WS2812B_ST_PREFIX;
            }
            break;
        case WS2812B_ST_SHDN:
        default:
            WS2812B_GPIO_DISABLE;
                
            st = WS2812B_ST_DISABLED;
            break;
    }
    
    return st;
}

inline void WS2812BWrite(WS2812B_CONFIG cfg)
{
    uint8_t cG = cfg.green, cR = cfg.red, cB = cfg.blue;
    WS2812B_WriteWord(cG);
    WS2812B_WriteWord(cR);
    WS2812B_WriteWord(cB);
}

inline void WS2812B_WriteWord( uint8_t sym )
{
    switch(sym)
    {
        default:
        case WS2812B_OFF: WS2812B_WriteWord_OFF(); break;
        case WS2812B_LOW: WS2812B_WriteWord_LOW(); break;
        case WS2812B_MED: WS2812B_WriteWord_MED(); break;
        case WS2812B_HIGH: WS2812B_WriteWord_HIGH(); break;
    }
}

inline void WS2812B_WriteWord_OFF( void ) // 00
{
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
}

inline void WS2812B_WriteWord_LOW( void ) // 04
{
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymOne();
    WS2812B_WriteSymZero();
    
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
}

inline void WS2812B_WriteWord_MED( void ) // 08
{
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymOne();
    
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
}

inline void WS2812B_WriteWord_HIGH( void ) // 16
{
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    
    WS2812B_WriteSymOne();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
    WS2812B_WriteSymZero();
}

inline void WS2812B_WriteSymZero( void )
{
    WS2812B_GPIO_SET;
    WS2812B_GPIO_CLR;
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
}

inline void WS2812B_WriteSymOne( void )
{
    WS2812B_GPIO_SET;
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    __NOP();
    WS2812B_GPIO_CLR;
}

/* [] END OF FILE */
